package fr.inserm.umr915.vcfserver.bio.bam;


import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import fr.inserm.umr915.vcfserver.bio.Segment;
import fr.inserm.umr915.vcfserver.bio.samples.Sample;

import net.sf.samtools.Cigar;
import net.sf.samtools.CigarElement;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMFileReader.ValidationStringency;
import net.sf.samtools.util.CloseableIterator;


@SuppressWarnings("serial")
public class ShowBamServlet
	extends HttpServlet
	{
	private BufferedImage bamImage(
			File file,
			Segment segment,
			Integer highlight
			) throws Exception
			{
			if(segment.getChromStart()<0 || segment.getChromStart()>=segment.getChromEnd() )
				{
				throw new IllegalArgumentException("bad chromStart:"+segment);
				}
			
			
			if( file==null ||
				!file.exists() ||
				!file.isFile() || 
				!file.canRead() ||
				!file.getName().toLowerCase().endsWith(".bam"))
				{
				throw new IllegalArgumentException("bad file:"+file);
				}

			SAMFileReader inputSam=null;
			List<SAMRecord> records=new ArrayList<SAMRecord>();
			CloseableIterator<SAMRecord> iter=null;
			
			
			try {
				inputSam=new SAMFileReader(file);
				inputSam.setValidationStringency(ValidationStringency.SILENT);
				

				iter= inputSam.queryOverlapping(
					segment.getChromosome(),
					segment.getChromStart(),
					segment.getChromEnd()
					);			
				
				while (iter.hasNext())
			     	{
					
					final SAMRecord samRecord =iter.next();
					if(samRecord.getReadUnmappedFlag()) continue;
					records.add(samRecord);
			     	}
				final int fontHeight=14;
				final String fontFamily="Courier";
				Font font=new Font(fontFamily, Font.PLAIN, fontHeight);
				BufferedImage img=new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
				Graphics g1=img.getGraphics();
				FontMetrics fm=g1.getFontMetrics(font);
				g1.dispose();
				
				final int marginy=2;
				int cx=fm.getMaxAdvance()+2;
				int cy=fm.getHeight()+marginy;
				
				img=new BufferedImage(
						cx*(segment.getChromEnd() - segment.getChromStart()),
						Math.max(cy,cy*(records.size())),
						BufferedImage.TYPE_INT_ARGB
						);
				
				int y=0;
				Graphics2D g=(Graphics2D)img.getGraphics();
				g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				if(highlight!=null)
					{
					int x= (highlight-segment.getChromStart())*cx;
					g.setColor(Color.LIGHT_GRAY);
					g.fillRect(x, 1, cx, img.getHeight()-1);
					g.setColor(Color.RED);
					g.drawRect(x, 1, cx, img.getHeight()-1);
					}
				g.setColor(Color.BLACK);
				for(SAMRecord rec:records)
					{
					Cigar cigar=rec.getCigar();
					
					byte cigarBytes[]=rec.getReadBases();
					int byte_index=0;
					
					int pos=rec.getUnclippedStart()-segment.getChromStart();
					
					for(CigarElement elt:cigar.getCigarElements())
						{
						int cl=elt.getLength();
						switch(elt.getOperator())
							{
							case M:
								{
								while(cl>0)
								    {
									g.drawString(String.valueOf((char)cigarBytes[byte_index]), pos*cx,y+ fm.getHeight());
									
									byte_index++;
								    cl--;
								    ++pos;
								    }
								break;
								}
							case H:break; /* ignore */
						    case S:break; /* ignore */
						    case P: //...
						    case N://....
						    case D:
								{
								/* Spans positions, No Coverage */
									while(cl>0)
								    {
									pos++;
									cl--;
								    }
								break;
								}
						    case I: /* cursor not moved on reference */ ;
						    	{
						    	g.setColor(Color.GREEN);
						    	g.drawRect(pos*cx-2, y, 2,cy);
						    	g.setColor(Color.BLACK);
						    	while(cl>0)
								    {
						    		byte_index++;
									cl--;
								    }
						    	break;
						    	}
						    default: throw new IllegalArgumentException(elt.getOperator().toString());

							}
						}
					//System.err.println( " "+rec.getCigarString()+" "+rec.getAlignmentStart()+" "+segment.getChromStart()+" "+y);
					y+=cy;
					}
				
				g.setColor(Color.BLACK);
				g.drawRect(0, 0, img.getWidth()-1, img.getHeight()-1);
				g.dispose();
				
				return img;
				}
			catch (Exception err)
				{
				err.printStackTrace();
				throw err;
				}
			finally
				{
				if(iter!=null) try {iter.close();} catch(Exception err){}
			    if(inputSam!=null) try {inputSam.close();} catch(Exception err){}
			    }
			}

	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
			{
			doGet(req, resp);
			}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
			{
			OutputStream out=null;
			try
				{
				Sample sample=(Sample)req.getAttribute("sample");
				if(sample==null)
					{
					fatalError("sample missing",req,resp);
					return;
					}
				if(sample.getBamFile()==null)
					{
					fatalError("sample.bam missing",req,resp);
					return;
					}
				
				Segment segment=(Segment)req.getAttribute("segment");
				if(segment==null)
					{
					fatalError("segment missing",req,resp);
					return;
					}
				
				
				Integer highlight=null;
				try {
					String s=req.getParameter("highlight");
					if(!(s==null || s.trim().isEmpty()))
						{
						highlight=Integer.parseInt(s);
						}
					}
				catch(NumberFormatException err)
					{
					highlight=null;
					}
				
				
				
				BufferedImage img= bamImage(sample.getBamFile(), segment, highlight);
				out= resp.getOutputStream();
				String format= req.getParameter("format");
				if(format==null) format="PNG";
				if(format.equals("jpg") || format.equals("jpeg"))
					{
					resp.setContentType("image/jpg");
					ImageIO.write(img, "JPG", out);
					}
				else 
					{
					resp.setContentType("image/png");
					ImageIO.write(img, "PNG", out);
					}
				}
			catch(IOException err)
				{
				err.printStackTrace();
				throw err;
				}
			catch(Throwable err)
				{
				err.printStackTrace();
				throw new IOException(err);
				}
			finally
				{
				if(out!=null) try { out.flush();}catch(IOException err) {}
				if(out!=null) try { out.close();}catch(IOException err) {}
				}
			}
	/** something wrong happened */
	private void fatalError(String message,HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException
		{
		resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		resp.setContentType("text/plain");
		PrintWriter out=resp.getWriter();
		out.println(message);
		out.flush();
		out.close();
		}
	}